package service

import (
	"errors"
	"fmt"
	"ssdevice/common/constant"
	"ssdevice/common/cron"
	cDto "ssdevice/common/dto"
	"ssdevice/common/middleware/mqtt"
	"ssdevice/common/utils"
	"ssdevice/internal/device/models"
	"ssdevice/internal/device/models/query"
	"time"

	"gitee.com/sansaniot/ssiot-core/facade/env"
	"gitee.com/sansaniot/ssiot-core/logger"
	"github.com/bytedance/sonic"
)

func StartTaskScheduler() {
	// 定时检查任务超时情况
	cron.AddCronTask("0/30 * * * * ?", monitorTaskStatus, false)
	// 添加任务扫描
	cron.AddCronTask("0/2 * * * * ?", scheduleTask, false)
}

// 扫描任务执行
func scheduleTask() {
	s := &DeviceTask{}
	s.Orm = env.Db
	s.SetAllModel()

	// 查询需要执行的批量任务
	req := query.SysBatchTaskQuery{Status: constant.BatchTaskRunning}
	err, result := s.sysBatchTask.FindTaskList(&req)
	if err != nil {
		return
	}

	batchTaskList := result.([]models.SysBatchTask)
	logger.Debugf("running batch task num: %d", len(batchTaskList))

	// 对超时的任务安排重试，并执行 pending 且到时间的子任务
	for _, batchTask := range batchTaskList {
		if err = s.processBatchTask(batchTask); err != nil {
			logger.Errorf("device task %s process failed: %v", batchTask.Id, err)
		}
	}
}

// 执行批量任务
func (d *DeviceTask) processBatchTask(task models.SysBatchTask) (err error) {
	// 有重试次数的任务，处理其子任务
	if task.RetryCount > 0 {
		_ = d.scheduleTaskRetry(task)
	}

	err = d.dispatchTask(task)
	return nil
}

func monitorTaskStatus() {
	s := &DeviceTask{}
	s.Orm = env.Db
	s.SetAllModel()

	req := query.SysBatchTaskQuery{}
	req.Status = constant.BatchTaskRunning
	err, result := s.sysBatchTask.FindTaskList(&req)
	if err != nil {
		return
	}

	taskList := result.([]models.SysBatchTask)

	for _, task := range taskList {
		_ = s.updateDeviceTaskStatus(task)
	}
}

// 执行批量任务
func (d *DeviceTask) dispatchTask(task models.SysBatchTask) error {
	switch task.TaskType {
	case constant.BatchTaskTypeUpgrade:
		return d.handleUpgradeTask(task)
	case constant.BatchTaskTypeRestart:
		return d.handleRestartTask(task)
	case constant.BatchTaskTypeConfig:
		return d.handleConfigTask(task)
	case constant.BatchTaskTypeSensorSet:
		return d.handleSenserSetTask(task)
	default:
		return errors.New("unknown task type")
	}
}

// 新增重试处理逻辑
func (d *DeviceTask) scheduleTaskRetry(bathTask models.SysBatchTask) error {
	var childTasks []models.SysTaskDevice
	if err := d.sysTaskDevice.FindList(&childTasks,
		cDto.MakeCondition(query.SysTaskDeviceQuery{
			TaskId:   bathTask.Id,
			StatusIn: []string{constant.DeviceTaskTimeout},
		})); err != nil {

		return err
	}

	if len(childTasks) > 0 {
		logger.Debugf("scheduling %d retry device tasks of batch task %s", len(childTasks), bathTask.Id)
	} else {
		return nil
	}

	for _, child := range childTasks {
		if child.RetryAttempt >= bathTask.RetryCount {
			continue
		}

		// 计算下次重试间隔
		interval := time.Duration(bathTask.RetryInterval) * time.Second

		// 更新任务状态
		child.RetryAttempt++
		child.Status = constant.DeviceTaskPending
		nextRetry := cDto.LocalTime(time.Now().Add(interval))
		child.PlanExecTime = &nextRetry

		if err := d.sysTaskDevice.Mod(child); err != nil {
			logger.Errorf("update retry task failed: %v", err)
			continue
		}
	}

	return nil
}

// 处理升级任务
func (d *DeviceTask) handleUpgradeTask(task models.SysBatchTask) error {
	// 查找升级包
	var otaPackage models.SysOTAPackage
	packageQuery := query.SysOTAPackageQuery{Id: task.PackageId}
	if err := d.sysOTAPackage.FindOne(&otaPackage, cDto.MakeCondition(packageQuery.GetNeedSearch())); err != nil {
		return fmt.Errorf("failed to find OTA package: %v", err)
	}

	// 准备下载参数
	prefix := env.ConfigOperator.GetString("service.device.download-prefix")
	url := prefix + "/api/file/download/" + otaPackage.PackagePath

	// 下载文件
	err, result := utils.DownloadFile(url)
	if err != nil {
		return fmt.Errorf("file download failed: %v", err)
	}
	logger.Infof("OTA package download completed, package ID: %s", otaPackage.Id)

	// 获取子任务列表
	childTasks, err := d.getPendingChildTasks(task.Id)
	if err != nil {
		return err
	}

	if len(childTasks) > 0 {
		logger.Infof("executing %d pending upgrade device tasks for batch task %s", len(childTasks), task.Id)
	}

	// 构建MQTT消息
	childMqttUpgradeList := make([]map[string]interface{}, 0, len(childTasks))
	for _, taskDevice := range childTasks {
		if err = d.updateChildTaskStatus(&taskDevice, constant.DeviceTaskRunning); err != nil {
			logger.Errorf("更新任务状态失败: %v", err)
		}

		content := map[string]interface{}{
			"sid": taskDevice.Sid,
			"data": map[string]interface{}{
				"type":   "main",
				"url":    url,
				"fileid": otaPackage.PackagePath,
				"md5":    result["md5sum"],
				"size":   result["fileSize"],
			},
			"sn": taskDevice.DevSn,
		}
		childMqttUpgradeList = append(childMqttUpgradeList, content)
	}

	// 发送MQTT消息
	for i, content := range childMqttUpgradeList {
		topic := fmt.Sprintf("/iot/%s/%s/ota/upgrade", content["sn"], constant.TopicPlaceholder)
		content["sn"] = nil // 移除临时存储的sn
		contentBytes, _ := sonic.Marshal(content)

		if err := mqtt.MqttClient.GetClient().Publish(topic, 1, false, contentBytes).Error(); err != nil {
			logger.Errorf("topic %s message publish failed: %s", topic, err.Error())
		}

		// 控制发送频率
		if i%10 == 0 {
			time.Sleep(9 * time.Millisecond)
		}
	}
	return nil
}

// 处理重启任务
func (d *DeviceTask) handleRestartTask(task models.SysBatchTask) error {
	childTasks, err := d.getPendingChildTasks(task.Id)
	if err != nil {
		return err
	}

	if len(childTasks) > 0 {
		logger.Infof("executing %d pending restart device tasks for batch task %s", len(childTasks), task.Id)
	}

	for _, childTask := range childTasks {
		if err = d.updateChildTaskStatus(&childTask, constant.DeviceTaskRunning); err != nil {
			continue
		}

		content := map[string]interface{}{
			"sid":  childTask.Sid,
			"data": map[string]interface{}{"input": nil},
		}

		topic := fmt.Sprintf("/iot/%s/%s/func/reboot", childTask.DevSn, constant.TopicPlaceholder)
		subT := fmt.Sprintf("/iot/%s/%s/func/reboot/ack", constant.TopicPlaceholder, childTask.DevSn)

		if err = d.sendMqttCommand(childTask, topic, content, subT, constant.OverTimeSecond*time.Second); err != nil {
			syncSendErrCallback(subT, int(childTask.Sid))

			if err.Error() == "下发超时" {
				childTask.Status = constant.DeviceTaskTimeout
			} else {
				childTask.Status = constant.DeviceTaskFailed
			}

			if err = d.sysTaskDevice.Mod(childTask); err != nil {
				logger.Errorf("failed to update failure status: %v", err)
			}
		}
	}
	return nil
}

// 处理配置任务
func (d *DeviceTask) handleConfigTask(task models.SysBatchTask) error {
	childTasks, err := d.getPendingChildTasks(task.Id)
	if err != nil {
		return err
	}

	configData := utils.BytesToMap(task.ConfigData)

	if len(childTasks) > 0 {
		logger.Infof("executing %d pending config device tasks for batch task %s", len(childTasks), task.Id)
	}

	for _, childTask := range childTasks {
		if err = d.updateChildTaskStatus(&childTask, constant.DeviceTaskRunning); err != nil {
			continue
		}

		topic := fmt.Sprintf("/iot/%s/%s/config/set", childTask.DevSn, constant.TopicPlaceholder)
		subT := fmt.Sprintf("/iot/%s/%s/config/set/ack", constant.TopicPlaceholder, childTask.DevSn)
		content := map[string]interface{}{
			"sid":  childTask.Sid,
			"data": configData,
		}

		if err = d.sendMqttCommand(childTask, topic, content, subT, constant.OverTimeSecond*time.Second); err != nil {
			syncSendErrCallback(subT, int(childTask.Sid))

			if err.Error() == "下发超时" {
				childTask.Status = constant.DeviceTaskTimeout
			} else {
				childTask.Status = constant.DeviceTaskFailed
			}

			if err = d.sysTaskDevice.Mod(childTask); err != nil {
				logger.Errorf("failed to update failure status: %v", err)
			}
		}
	}

	return nil
}

// 处理传感量下发任务
func (d *DeviceTask) handleSenserSetTask(task models.SysBatchTask) error {
	childTasks, err := d.getPendingChildTasks(task.Id)
	if err != nil {
		return err
	}

	configData := utils.BytesToMap(task.ConfigData)

	if len(childTasks) > 0 {
		logger.Infof("executing %d pending sensor set device tasks for batch task %s", len(childTasks), task.Id)
	}

	for _, childTask := range childTasks {
		if err = d.updateChildTaskStatus(&childTask, constant.DeviceTaskRunning); err != nil {
			continue
		}

		topic := fmt.Sprintf("/iot/%s/%s/sensor/set", childTask.DevSn, constant.TopicPlaceholder)
		subT := fmt.Sprintf("/iot/%s/%s/sensor/set/ack", constant.TopicPlaceholder, childTask.DevSn)
		content := map[string]interface{}{
			"sid":  childTask.Sid,
			"data": configData,
		}

		if err = d.sendMqttCommand(childTask, topic, content, subT, constant.OverTimeSecond*time.Second); err != nil {
			syncSendErrCallback(subT, int(childTask.Sid))

			if err.Error() == "下发超时" {
				childTask.Status = constant.DeviceTaskTimeout
			} else {
				childTask.Status = constant.DeviceTaskFailed
			}

			if err = d.sysTaskDevice.Mod(childTask); err != nil {
				logger.Errorf("failed to update failure status: %v", err)
			}
		}
	}
	return nil
}

// 新增公共方法：获取待处理的子任务
func (d *DeviceTask) getPendingChildTasks(taskId string) ([]models.SysTaskDevice, error) {
	var childTasks []models.SysTaskDevice
	err := d.sysTaskDevice.FindList(&childTasks, cDto.MakeCondition(query.SysTaskDeviceQuery{
		TaskId:          taskId,
		Status:          constant.DeviceTaskPending,
		PlanExecTimeLte: cDto.LocalTime(time.Now()),
	}))
	return childTasks, err
}

// 新增公共方法：更新子任务状态
func (d *DeviceTask) updateChildTaskStatus(childTask *models.SysTaskDevice, status string) error {
	now := cDto.LocalTime(time.Now())
	childTask.UpdateTime = &now
	childTask.ExecTime = &now
	childTask.Status = status
	return d.sysTaskDevice.Mod(childTask)
}

// 新增公共方法：发送MQTT命令（带回调）
func (d *DeviceTask) sendMqttCommand(childTask models.SysTaskDevice, topic string, content map[string]interface{},
	subT string, overtime time.Duration) error {

	contentBytes, _ := sonic.Marshal(content)

	if err := mqtt.MqttClient.SyncSendTimer(topic, contentBytes, int(childTask.Sid), overtime, subT, func(msgInfo map[string]interface{}, e *error) {
		if err := d.updateChildTaskStatus(&childTask, constant.DeviceTaskDone); err != nil {
			logger.Errorf("updating device stask to done failed: %v", err)
		}
	}); err != nil {
		syncSendErrCallback(subT, int(childTask.Sid))
		return err
	}
	return nil
}

// chunkSlice 将一个切片分割成多个大小为chunkSize的子切片
func chunkSlice(slice []models.SysTaskDevice, chunkSize int) [][]models.SysTaskDevice {
	var chunks [][]models.SysTaskDevice
	for i := 0; i < len(slice); i += chunkSize {
		end := i + chunkSize
		if end > len(slice) {
			end = len(slice)
		}
		chunks = append(chunks, slice[i:end])
	}
	return chunks
}

func (d *DeviceTask) updateDeviceTaskStatus(one models.SysBatchTask) (err error) {
	// 检查状态为 pending, running 的子任务
	queryReq := query.SysTaskDeviceQuery{TaskId: one.Id, StatusIn: []string{constant.DeviceTaskPending, constant.DeviceTaskRunning}}
	var result interface{}
	if err, result = d.sysTaskDevice.FindTaskDeviceList(&queryReq); err != nil {
		return err
	}
	childTaskList := result.([]models.SysTaskDevice)

	// 存在pending和running的子任务，则批量任务未完成
	finishFlag := true
	if len(childTaskList) > 0 {
		finishFlag = false
	}

	num := 0

	// 将超时的子任务设置为超时
	for _, childTask := range childTaskList {
		if childTask.Status == constant.DeviceTaskPending {
			continue
		}

		// 判断是否超时  超时则改变状态
		execTime := time.Time(*childTask.ExecTime)
		now := time.Now()
		difference := now.Sub(execTime)

		//执行中子任务  判断是否超时
		if difference.Minutes() > 5 {
			//超时
			childTask.Status = constant.DeviceTaskTimeout
			updateTime := cDto.LocalTime(time.Now())
			childTask.UpdateTime = &updateTime
			_ = d.sysTaskDevice.Mod(childTask)
			num++
		}
	}

	if num > 0 {
		logger.Infof("update %d device task status to timeout of batch task %s", num, one.Id)
	}

	//如果所有子任务都为非 doing状态 则任务修改为完成2
	if finishFlag {
		one.Status = constant.BatchTaskCompleted
		now := cDto.LocalTime(time.Now())
		one.UpdateTime = &now
		_ = d.sysBatchTask.Mod(one)
	}

	return nil
}
